package com.example.manager.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.manager.domain.R;
import com.example.manager.domain.SysMenu;
import com.example.manager.domain.SysUser;
import com.example.manager.filter.TokenFilter;
import com.example.manager.service.SysMenuService;
import com.example.manager.service.SysUserService;
import com.example.manager.utils.JwtUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

//enable
//disable
//伪链
@Configuration
public class SecurityConfig {

    @Autowired
    TokenFilter tokenFilter;

    @Autowired
    public SysUserService userService;

    @Autowired
    public SysMenuService menusService;
    @Autowired
    RedisTemplate<String, Object> redisTemplate2;

    ObjectMapper objectMapper = new ObjectMapper();


    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("123"));
    }

    /**
     * 创建spring的加密对象
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义SQL进行查询账户信息，而不是从security默认表中去读取用户
     *
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsService() {
            /**
             * 根据账户名加载用户信息
             * @param username
             * @return
             * @throws UsernameNotFoundException
             */
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                QueryWrapper<SysUser> userQuery = new QueryWrapper<>();
                userQuery.lambda().eq(SysUser::getUsername, username);
                SysUser users = userService.getOne(userQuery);
                if (users == null) {
                    throw new UsernameNotFoundException("账户或者密码错误");
                }
                List<SysMenu> menus = null;
                if(users.getUserId()!=1){
                    menus = menusService.findSysMenusByUserId(users.getUserId());
                }else{
                    menus = menusService.findSysMenus();
                }
                LoginUser loginUser = new LoginUser(users,menus);
//                loginUser.setUsers(users);//从后台加载出来的用户信息
//                loginUser.setMenus(menus);//从后台加载的权限信息
                return loginUser;
            }
        };
    }

    //cors()：允许跨域请求
    //disable(): 禁用
    //csrf().disable()：禁用伪链
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //将整个系统的权限加载到security中
        //只查询F类的功能权限，排除菜单
        QueryWrapper<SysMenu> query = new QueryWrapper<>();
        query.lambda().eq(SysMenu::getMenuType, "F");
        List<SysMenu> menus = menusService.list();//获取所有menus
        //.successHandler() : 登录成功执行此函数
        //.failureHandler():登录失败，执行此函数
        http.csrf().disable().cors().and().formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/submitLogin")
                //处理登录成功
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.setCharacterEncoding("utf-8");
                        //拿到登录的凭证
                        //用户信息和菜单信息（权限信息）
                        LoginUser user = (LoginUser) authentication.getPrincipal();
                        String token = JwtUtils.generateToken(user.getUsername());
                        String json = objectMapper.writeValueAsString(R.success(200, "登录成功", token));
                        //登录成功后，将当前登录的用户的权限列表写入到redis
                        redisTemplate2.opsForValue().set("login:"+user.getUsername(),user);
                        response.getWriter().print(json);
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.setCharacterEncoding("utf-8");
                        String json = objectMapper.writeValueAsString(R.fail(401, "账户或密码错误"));
                        response.getWriter().print(json);
                    }
                })
        ;

        http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                response.setCharacterEncoding("utf-8");
                String json = objectMapper.writeValueAsString(R.fail(401, "你还未登录，请先登录"));
                response.getWriter().print(json);
            }
        }).accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                response.setCharacterEncoding("utf-8");
                String json = objectMapper.writeValueAsString(R.fail(403, "你权限不足，请联系管理员"));
                response.getWriter().print(json);
            }
        });

        //加载上节课的权限列表
        menus.forEach(item->{
            if(item.getMenuType().equals("F") && StringUtils.hasLength(item.getUrl())){
                try {
                    http.authorizeRequests().antMatchers(item.getUrl()).hasAuthority(item.getPerms());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        //所有请求都需要登录之后才能进入
        http.authorizeRequests().anyRequest().authenticated();
        //改变过滤器的验证顺序
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }



}
